<?php

/**
 * REQUEST请求封装<p/>
 * @catepory【核心】REQUEST请求
 * @author zhouhaichao(a)2008.sina.com
 * @version 1.0
 * @copyright (c) 2008
 * @date 2013-2-21
 */
class Request
{
    // 请求
    public $requestUri; // 请求UR（处理过，不包含项目名称）REQUEST_URI
    public $params = array(); // 请求参数POST/GET都会放入其中
    public $attrs = array(); // 模仿jsp ,setAttribute

    // 请求处理器参数
    public $controller = NULL; // 处理器
    public $action = NULL; // 处理器处理方法
    public $methodParam = array(); // 请求方法的参数
    public $mimeType = "";

    public $isAction = true;

    /**
     * 构造方法
     */
    public function __construct()
    {
        // 解析URL
        $this->requestUri = $this->_detect_uri();
        //访问后缀
        $this->mimeType = $this->_detect_extension($this->requestUri);
        // 解析对应控制器
        $this->_detect_action();
        //解析所有参数到param中
        $this->_detect_params();
    }

    /**
     * 访问后缀
     * @param $file
     * @return mixed|null
     */
    protected function _detect_extension($file)
    {
        $ext = pathinfo($file, PATHINFO_EXTENSION);
        if ($ext) {
            return $ext;
        } else {
            return "";
        }
    }

    /**
     * 解析用户的URL到Controller
     */
    protected function _detect_action()
    {
        $config = Config::getInstance();
        if ($this->requestUri == "/") //主页，使用配置的默认页面去显示
        {
            $this->controller = $config->getConfig("default_controller");
            $this->action = $config->getConfig("default_controller_method");
        } else {
            $uri = str_replace(array('.' . $this->mimeType), '', $this->requestUri);
            $route = $config->getConfig("route", array());
            $routeUri = $this->getRoute($route, $uri);
            $mca = explode("/", $routeUri);
            $len = count($mca);
            switch ($len) {
                case 1: {
                    $this->controller = $mca[0];
                    $this->action = $config->getConfig("default_controller_method");
                    $this->methodParam = array();
                }break;

                case 2: {
                    $this->controller = $mca[0];
                    $this->action = $mca[1];
                    $this->methodParam = array();
                }break;

                default: {
                    $this->controller = $mca[0];
                    $this->action = $mca[1];
                    $this->methodParam = array_splice($mca, 2, $len);
                }break;
            }
        }
    }

    /**
     * 获得路由
     * @param array $route 路由表
     * @param string $url 访问URL
     * @return string 结果URL
     */
    protected function getRoute($route, $url)
    {
        if ($route && isset($route[$url]) && $route[$url]) {
            $url = $route[$url];
        } else {
            foreach ($route as $k => $v) {
                $pattern = str_replace("*", "(\\w+)", $k);
                $pattern = str_replace("/", "\/", $pattern);
                $pattern = "/^" . $pattern . "$/i";
                if (preg_match($pattern, $url)) {
                    $url = preg_replace($pattern, $v, $url);
                    return $url;
                }
            }
        }
        return $url;
    }


    /**
     * 返回控制器的各项执行参数
     * @return array
     *  path:控制器的文件路径,
     *  controller：控制器类名，
     *  method:控制器的方法，
     *  param:控制器执行的方法，
     *  mimeType:访问的后缀
     */
    public function getController()
    {
        $controllerClassName = ucwords($this->controller) . "Controller";
        $controllerClassFile = ROOT . "/application/controller/$controllerClassName.class.php";

        return array(
            "path" => $controllerClassFile,
            "controller" => $controllerClassName,
            "method" => $this->action,
            "param" => $this->methodParam,
            "mimeType" => $this->mimeType
        );
    }

    /**
     * 解析参数
     */
    protected function _detect_params()
    {
        foreach ($_POST + $_GET as $k => $v) {
            $this->params[$k] = $v;
        }
    }

    /**
     * 获得输入参数，POST/GET参数都会放入其中
     *
     * @param string $name
     * @param object $def
     * @return object value
     */
    public function getParamter($name, $def = NULL)
    {
        if (isset($this->params[$name])) {
            return $this->params[$name];
        } else {
            return $def;
        }
    }

    /**
     * 类似于jsp request.getAttribute()
     *
     * @param string $name
     * @param object $def
     * @return object value
     */
    public function getAttribute($name, $def = NULL)
    {
        if (isset($this->attrs[$name])) {
            return $this->attrs[$name];
        } else {
            return $def;
        }
    }

    /**
     * 类似于jsp request.getAttribute()
     * 设置进去的参数会传给页面
     */
    public function setAttribute($name, $value)
    {
        $this->attrs[$name] = $value;
    }

    /**
     * Detects the URI
     *
     * This function will detect the URI automatically and fix the query string
     * if necessary.
     *
     * @access protected
     * @return string
     */
    protected function _detect_uri()
    {
        if (!isset($_SERVER['REQUEST_URI']) or !isset($_SERVER['SCRIPT_NAME'])) {
            return '/';
        }

        $uri = $_SERVER['REQUEST_URI'];

        if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0) {
            $uri = substr($uri, strlen($_SERVER['SCRIPT_NAME']));
        } elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0) {
            $uri = substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME'])));
        }

        // This section ensures that even on servers that require the URI to be
        // in the query string (Nginx) a correct
        // URI is found, and also fixes the QUERY_STRING server var and $_GET
        // array.
        if (strncmp($uri, '?/', 2) === 0) {
            $uri = substr($uri, 2);
        }
        $parts = preg_split('#\?#i', $uri, 2);
        $uri = $parts[0];
        if (isset($parts[1])) {
            $_SERVER['QUERY_STRING'] = $parts[1];
            parse_str($_SERVER['QUERY_STRING'], $_GET);
        } else {
            $_SERVER['QUERY_STRING'] = '';
            $_GET = array();
        }

        if ($uri == '/' || empty($uri)) {
            return '/';
        }

        $uri = parse_url($uri, PHP_URL_PATH);

        // Do some final cleaning of the URI and return it
        return str_replace(array('//', '../'), '/', trim($uri, '/'));
    }


    /**
     * 判断是不是GET提交
     */
    public function isGet()
    {
        return $_SERVER['REQUEST_METHOD'] == 'GET';
    }

    /**
     * 判断是不是POST提交
     */
    public function isPost()
    {
        return $_SERVER['REQUEST_METHOD'] == 'POST';
    }

    /**
     * 判断是不是wap浏览器
     */
    public function isWap()
    {
        $agent = $_SERVER['HTTP_USER_AGENT'];
        if (strpos($agent, "NetFront") ||
            strpos($agent, "iPhone") ||
            strpos($agent, "MIDP-2.0") ||
            strpos($agent, "Opera Mini") ||
            strpos($agent, "UCWEB") ||
            strpos($agent, "Android") ||
            strpos($agent, "Windows CE") ||
            strpos($agent, "SymbianOS")
        ) {
            return true;
        } else {
            return false;
        }
    }


    /**
     * Header Redirect
     *
     * Header redirect in two flavors
     * For very fine grained control over headers, you could use the Output
     * Library's set_header() function.
     *
     * @access public
     * @param string    the URL
     * @param string    the method: location or redirect
     * @return string
     */
    public function redirect($uri = '', $method = 'location', $http_response_code = 302)
    {
        if (!preg_match('#^https?://#i', $uri)) {
            $uri = CTX . $uri;
        }
        switch ($method) {
            case 'refresh' :
                header("Refresh:0;url=" . $uri);
                break;
            default :
                header("Location: " . $uri, TRUE, $http_response_code);
                break;
        }
        exit();
    }

    /**
     * 显示404页面，
     */
    public function show_404()
    {
        @header('Status 404 Not Found ' . $this->requestUri, true, 404);
        $config = Config::getInstance();
        $page404 = $config->getConfig(array('error_page', '404'));
        extract(array(
            "req" => $this,
            "conf" => Config::getInstance(),
            "param" => $this->params
        ));
        require_once ROOT . $page404;
        exit();
    }

    /**
     * 显示500页面，
     * @param Exception $e
     */
    public function show_500($e)
    {
        @header('Status 500 ' . $e->getMessage(), true, 500);
        $config = Config::getInstance();
        $page500 = $config->getConfig(array('error_page', '500'));
        extract(array(
            "req" => $this,
            "conf" => Config::getInstance(),
            "param" => $this->params,
            "error" => $e
        ));
        require_once ROOT . $page500;
        exit();
    }
}